package com.lzh.excel.imports.listener;

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.util.StringUtils;
import com.lzh.excel.exception.ErrorCode;
import com.lzh.excel.exception.ExcelException;

import java.lang.reflect.Field;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @author zhehen.lu
 * @date 2025/8/3 14:30
 */
public abstract class BaseAbstractImportListener<T> extends AnalysisEventListener<T> {
    protected int batchCount = 2000;
    protected BaseAbstractImportListener.CheckParamsType checkParamsType;
    protected List<String> errorMsgList;
    protected List<T> dataList;
    protected int fieldNum;
    protected int totalHeadLineNum;
    protected int totalDataLineNum;
    protected boolean enableEmptyDeal;
    protected int realHeadLine;
    protected int needDataLine;

    public BaseAbstractImportListener() {
        this.checkParamsType = BaseAbstractImportListener.CheckParamsType.ALL_LINE_AGGREGATION;
        this.errorMsgList = new ArrayList();
        this.dataList = new ArrayList(this.batchCount);
        this.fieldNum = 0;
        this.totalHeadLineNum = 0;
        this.totalDataLineNum = 0;
        this.enableEmptyDeal = false;
        this.realHeadLine = 1;
        this.needDataLine = -1;
    }

    @Override
    public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
        ++this.totalHeadLineNum;
        if (context.readRowHolder().getRowIndex() == this.realHeadLine - 1) {
            if (this.fieldNum == 0 && context.readWorkbookHolder().getClazz() != null && !Map.class.isAssignableFrom(context.readWorkbookHolder().getClazz())) {
                int needReadFieldNum = 0;
                Field[] declaredFields = context.readWorkbookHolder().getClazz().getDeclaredFields();
                Field[] fields = declaredFields;
                int length = declaredFields.length;

                for(int i = 0; i < length; ++i) {
                    Field declaredField = fields[i];
                    if (declaredField.isAnnotationPresent(ExcelProperty.class)) {
                        ++needReadFieldNum;
                    }
                }

                this.fieldNum = needReadFieldNum;
            }

            this.headDeal(headMap, context);
        }

        if (this.enableEmptyDeal) {
            context.readWorkbookHolder().setIgnoreEmptyRow(false);
        }

    }

    @Override
    public void invoke(T importBo, AnalysisContext context) {
        ++this.totalDataLineNum;
        List<String> errorMsgList = new ArrayList();
        switch(this.checkParamsType) {
            case ONE_LINE:
                this.checkParamsAndSetError(importBo, context, errorMsgList);
                this.dataList.add(importBo);
                break;
            case ALL_LINE:
                this.dataList.add(importBo);
                break;
            case ALL_LINE_AGGREGATION:
            case ERROR_RECORD:
                this.checkParams(importBo, context, errorMsgList);
        }

        if (this.needDataLine != -1 && this.totalDataLineNum <= this.needDataLine) {
            this.invokeSpecialData(importBo, context);
        }

        if (this.dataList.size() >= this.batchCount) {
            this.saveData(context);
        }

    }

    protected void invokeErrorData(T importBo, AnalysisContext context, List<String> errorMsgList) {
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        if (context.readSheetHolder().getApproximateTotalRowNumber() == null || context.readSheetHolder().getApproximateTotalRowNumber() <= 1) {
            this.dealBlankTemplate(context);
        }

        this.saveData(context);
        this.doFinishAllAnalysedAfter(context);
    }

    @Override
    public void onException(Exception exception, AnalysisContext context) throws Exception {
        if (this.checkParamsType != BaseAbstractImportListener.CheckParamsType.ONE_LINE && exception instanceof ExcelDataConvertException) {
            ExcelDataConvertException dataConvertException = (ExcelDataConvertException)exception;
            Field field = dataConvertException.getExcelContentProperty().getField();
            ExcelProperty excelProperty = (ExcelProperty)field.getAnnotation(ExcelProperty.class);
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append("第").append(dataConvertException.getColumnIndex() + 1).append("列");
            if (excelProperty != null && excelProperty.value().length > 0 && !StringUtils.isEmpty(excelProperty.value()[0])) {
                stringBuilder.append(excelProperty.value()[0]);
            }

            stringBuilder.append("数据格式不对,");
            Class<?> type = field.getType();
            if (Integer.class.isAssignableFrom(type)) {
                stringBuilder.append("应该为整形,最大值为").append(2147483647);
            } else if (Float.class.isAssignableFrom(type)) {
                stringBuilder.append("应该为浮点形,最大值为").append(3.4028235E38F);
            } else if (Double.class.isAssignableFrom(type)) {
                stringBuilder.append("应该为浮点形,最大值为").append(1.7976931348623157E308D);
            } else if (Short.class.isAssignableFrom(type)) {
                stringBuilder.append("应该为整形,最大值为").append(32767);
            } else if (Long.class.isAssignableFrom(type)) {
                stringBuilder.append("应该为整形,最大值为").append(9223372036854775807L);
            } else if (Date.class.isAssignableFrom(type)) {
                stringBuilder.append("应该为日期,格式为2019-05-05 10:00:00");
            } else if (LocalDate.class.isAssignableFrom(type)) {
                stringBuilder.append("应该为日期,格式为2019-05-05");
            }

            switch(this.checkParamsType) {
                case ALL_LINE:
                case ALL_LINE_AGGREGATION:
                    stringBuilder.append("请核对下该列所有的格式是否正确");
                    this.setErrorMsg(stringBuilder.toString());
                    break;
                case ERROR_RECORD:
                    this.invokeException(exception, context, stringBuilder.toString());
            }

        } else {
            super.onException(exception, context);
        }
    }

    protected void invokeException(Exception exception, AnalysisContext context, String errorMsg) {
    }

    protected void doFinishAllAnalysedAfter(AnalysisContext context) {
    }

    protected void saveData(AnalysisContext context) {
        this.doSaveData(context, this.dataList);
        this.dataList.clear();
    }

    protected abstract void doSaveData(AnalysisContext var1, List<T> var2);

    protected abstract boolean checkParamsAndSetError(T var1, AnalysisContext var2, List<String> var3);

    protected void headDeal(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
        if (headMap == null) {
            throw new ExcelException(ErrorCode.EXCEL_NOT_EMPTY);
        } else if (headMap.size() != this.fieldNum && context.readWorkbookHolder().getClazz() != null && !Map.class.isAssignableFrom(context.readWorkbookHolder().getClazz())) {
            throw new ExcelException(ErrorCode.EXCEL_COLUMN_NUMBER_NOT_MATCH.getErrorCode(), "excel只能有" + this.fieldNum + "列");
        } else {
            this.invokeRealHead(headMap, context);
        }
    }

    protected void invokeRealHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
    }

    protected void invokeSpecialData(T importBo, AnalysisContext context) {
    }

    protected void dealBlankTemplate(AnalysisContext context) {
        throw new ExcelException(ErrorCode.EXCEL_EMPTY_TEMPLATE_NOT_ALLOW);
    }

    protected void setErrorMsg(String msg) {
        if (!this.errorMsgList.contains(msg)) {
            this.errorMsgList.add(msg);
        }

    }

    protected void setBatchCount(int count) {
        this.batchCount = count;
        this.dataList = new ArrayList(count);
    }

    protected void setFieldNum(int importFieldNum) {
        this.fieldNum = importFieldNum;
    }

    protected void setRealHeadLine(int realHeadLine) {
        this.realHeadLine = realHeadLine;
    }

    protected void setNeedDataLine(int needDataLine) {
        this.needDataLine = needDataLine;
    }

    protected int getTotalLine() {
        return this.totalDataLineNum + this.totalHeadLineNum;
    }

    private void checkParams(T importBo, AnalysisContext context, List<String> errorMsgList) {
        boolean isHaveError = this.checkParamsAndSetError(importBo, context, errorMsgList);
        if (isHaveError) {
            this.invokeErrorData(importBo, context, errorMsgList);
        } else {
            this.dataList.add(importBo);
        }

    }

    public void setCheckParamsType(BaseAbstractImportListener.CheckParamsType checkParamsType) {
        this.checkParamsType = checkParamsType;
    }

    public BaseAbstractImportListener.CheckParamsType getCheckParamsType() {
        return this.checkParamsType;
    }

    public List<String> getErrorMsgList() {
        return this.errorMsgList;
    }

    public int getTotalHeadLineNum() {
        return this.totalHeadLineNum;
    }

    public int getTotalDataLineNum() {
        return this.totalDataLineNum;
    }

    public boolean isEnableEmptyDeal() {
        return this.enableEmptyDeal;
    }

    public int getRealHeadLine() {
        return this.realHeadLine;
    }

    public int getNeedDataLine() {
        return this.needDataLine;
    }

    protected static enum CheckParamsType {
        ONE_LINE,
        ALL_LINE,
        ALL_LINE_AGGREGATION,
        ERROR_RECORD;

        private CheckParamsType() {
        }
    }
}